package com.lanchetech.im.service.impl;


import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.lanchetech.bean.dto.ChatDTO;
import com.lanchetech.bean.dto.MsgFeatureDTO;
import com.lanchetech.bean.util.PageHelpUtil;
import com.lanchetech.bean.vo.ChatVO;
import com.lanchetech.bean.vo.MessageVO;
import com.lanchetech.common.enums.MessageTypeEnum;
import com.lanchetech.common.enums.MsgReadStatusEnum;
import com.lanchetech.common.enums.WebConfigEnum;
import com.lanchetech.common.enums.WebsocketEventTypeEnum;
import com.lanchetech.dao.*;
import com.lanchetech.entity.*;
import com.lanchetech.im.config.JwtTokenUtil;
import com.lanchetech.im.config.WebSocketServer;
import com.lanchetech.im.service.ImService;
import com.lanchetech.service.DaoService;
import com.lanchetech.service.JpushService;
import com.lanchetech.service.PlaceholderResolver;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.yeauty.pojo.Session;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class ImServiceImpl implements ImService {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private AuthenticationManager authenticationManager;

    private UserDetailsService userDetailsService;

    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    MsgUserMapper msgUserMapper;

    @Autowired
    MsgMapper msgMapper;

    @Autowired
    DaoService daoService;

    @Autowired
    ShopMapper shopMapper;

    @Autowired
    JpushService jpushService;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    TenantMapper tenantMapper;

    @Autowired
    PlaceholderResolver placeholderResolver;

    @Autowired
    WebConfigMapper webConfigMapper;

    @Autowired
    public ImServiceImpl(
            AuthenticationManager authenticationManager,
            @Qualifier("customUserService") UserDetailsService userDetailsService,
            JwtTokenUtil jwtTokenUtil) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
    }


    /**
     * 生成响应字符串
     *
     * @param messageTypeEnum
     * @param data
     * @return
     */
    private static String buildMessage(MessageTypeEnum messageTypeEnum, Object data) {
        MessageVO vo = new MessageVO(messageTypeEnum.getType(), data);
        return JSON.toJSONString(vo);
    }

    /**
     * 首次连接路由
     *
     * @param userId
     * @param eventType
     * @param map
     * @param session
     */
    @Override
    public void imRouter(Long userId, String eventType, ConcurrentHashMap<Long, WebSocketServer> map, Session session) {
        try {
            switch (WebsocketEventTypeEnum.getByType(eventType)) {
                case USER_LOGIN:
                    userLogin(userId, session);
                    break;
                case LOGIN:
                    login(userId, session);
                    break;
                default:
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 消息路由
     *
     * @param userId
     * @param message
     * @param map
     * @param session
     */
    @Override
    public void messageRouter(Long userId, String message, ConcurrentHashMap<Long, WebSocketServer> map, Session session) {
        if (StringUtils.isNotBlank(message)) {
            try {
                //解析发送的报文
                JSONObject jsonObject = JSON.parseObject(message);
                switch (WebsocketEventTypeEnum.getByType(jsonObject.getString("eventType"))) {
                    case CHAT:
                        sendMessageToFriend(userId, jsonObject, map);
                        break;
                    case CHAT_HISTORY:
                        getChatHistory(userId, jsonObject.getIntValue("pageNo"), session);
                        break;
                    case CHAT_PAGE:
                        getChatPage(userId, jsonObject, map);
                        break;
                    case READ:
                        read(userId, jsonObject, map);
                        break;
                    default:
                        break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 用户登录
     *
     * @param userId
     * @param session
     * @throws IOException
     */
    @Override
    public void userLogin(Long userId, Session session) throws IOException {
        session.sendText(buildMessage(MessageTypeEnum.STATUS, "SUCCESS"));
    }

    /**
     * 登录
     *
     * @param userId
     * @param session
     * @throws IOException
     */
    @Override
    public void login(Long userId, Session session) throws IOException {
        Long count = msgMapper.countByIsReadAndToId(MsgReadStatusEnum.UNREAD.getStatus(), userId);
        session.sendText(buildMessage(MessageTypeEnum.UNREAD_COUNT, count == null ? "0" : count));
    }

    /**
     * 回复消息已读
     *
     * @param userId
     * @param map
     */
    @Override
    public void read(Long userId, JSONObject obj, ConcurrentHashMap<Long, WebSocketServer> map) throws IOException {
        Long friendId = obj.getLong("friendId");
        Long id = obj.getLong("id");
        // 单条标记为已读
        msgMapper.updateIsReadById(MsgReadStatusEnum.READ.getStatus(), id);

        if (map.containsKey(friendId)) {
            map.get(friendId).sendMessage(buildMessage(MessageTypeEnum.READ, userId));
        }
    }

    /**
     * 获取聊天历史
     *
     * @param userId
     * @param pageNo
     * @param session
     * @throws IOException
     */
    @Override
    public void getChatHistory(Long userId, int pageNo, Session session) throws IOException {
        Page page = PageHelper.startPage(pageNo, 10);
        List<ChatVO> chatVOList = msgMapper.findAllFriend(userId);

        if (!CollectionUtils.isEmpty(chatVOList)) {
            List<Long> friendIds = new ArrayList<>();
            chatVOList.stream().forEach(item -> {
                if (!userId.equals(item.getFromId())) {
                    friendIds.add(item.getFromId());
                    item.setFriendId(item.getFromId());
                }
                if (!userId.equals(item.getToId())) {
                    friendIds.add(item.getToId());
                    item.setFriendId(item.getToId());
                }
                // fix 修正如果自己于自己聊天
                if (userId.equals(item.getFromId()) && userId.equals(item.getToId())) {
                    friendIds.add(item.getToId());
                    item.setFriendId(item.getToId());
                }
            });

            Map<Long, MsgUser> map = daoService.getMsgUserMap(friendIds);
            chatVOList.stream().forEach(item -> {
                item.setAvatar(map.get(item.getFriendId()).getAvatar());
                item.setNickname(map.get(item.getFriendId()).getNickname());
                item.setServiceName(map.get(item.getFriendId()).getServiceName());
            });
        }
        session.sendText(buildMessage(MessageTypeEnum.CHAT_HISTORY, PageHelpUtil.buildPage(chatVOList, page)));
    }

    /**
     * 获取聊天记录分页
     */
    @Override
    public void getChatPage(Long userId, JSONObject obj, ConcurrentHashMap<Long, WebSocketServer> map) throws IOException {
        Long friendId = obj.getLong("friendId");
        Long endTime = obj.getLong("endTime");
        int pageNo = obj.getInteger("pageNo");
        Page page = PageHelper.startPage(pageNo, 10);
        List<Msg> msgList = msgMapper.findAllList(userId < friendId ? userId + ":" + friendId : friendId + ":" + userId, new Date(endTime));
        // 组装聊天对象数组
        List<ChatDTO> chatDTOS = new ArrayList<>();
        msgList.stream().forEach(item -> {
            Integer duration = 0;
            if ("voice".equals(item.getType())) {
                MsgFeatureDTO dto = JSON.parseObject(item.getFeature(), MsgFeatureDTO.class);
                duration = dto.getDuration();
            }
            chatDTOS.add(ChatDTO.builder()
                    .id(item.getId())
                    .content(item.getContent())
                    .type(item.getType())
                    .msgUserId(item.getFromId())
                    .userId(item.getFromId())
                    .friendId(item.getToId())
                    .timestamp(item.getCreatedAt())
                    .duration(duration)
                    .isRead(item.getIsRead())
                    .build());
        });

        if (pageNo == 1) {
            // 变成已读
            msgMapper.updateIsReadByFromIdAndToIdAndIsRead(MsgReadStatusEnum.READ.getStatus(), friendId, userId, MsgReadStatusEnum.UNREAD.getStatus());
            // 当我打开了聊天对话框，系统会发送消息给好友，说你已经打开了对话框。好友如果正打开着对话框，则所有未读立即变成已读
            if (friendId != null && map.containsKey(friendId)) {
                map.get(friendId).sendMessage(buildMessage(MessageTypeEnum.FRIEND_OPEN_CHAT, userId));
            }
        }
        log.debug(JSON.toJSONString(chatDTOS));
        map.get(userId).sendMessage(buildMessage(MessageTypeEnum.CHAT_MESSAGES, PageHelpUtil.buildPage(chatDTOS, page)));

        // 如果一条聊天记录都没有，则判断对方是否为客服，并自动发送一条消息
        if (pageNo == 1 && msgList.isEmpty()) {
            MsgUser friend = msgUserMapper.selectByPrimaryKey(friendId);
            MsgUser userObj = msgUserMapper.selectByPrimaryKey(userId);
            if (friend.getTenantId() != null) {
                String content = "";
                if (friend.getTenantId().equals(0L)) {
                    // 是平台客服
                    WebConfig platformReply = webConfigMapper.findOneByName(WebConfigEnum.AUTO_REPLY_FORMAT.getConfig());
                    if (platformReply != null && StringUtils.isEmpty(platformReply.getValue())) {
                        Map replyFormatMap = new HashMap();
                        replyFormatMap.put("shopName", "嗨跑平台");
                        replyFormatMap.put("serviceName", "小嗨在线客服");
                        replyFormatMap.put("phone", "400-1688-928");
                        content = placeholderResolver.resolveByMap(platformReply.getValue(), replyFormatMap);
                    } else {
                        content = "Hi，感谢你这么美丽还关注小嗨！欢迎来到小嗨的世界~\n" +
                                "全球好物应有尽有，欢迎选购哦~";
                    }
                } else {
                    // 是商家客服
                    Tenant tenant = tenantMapper.selectByPrimaryKey(friend.getTenantId());
                    String autoReplyFormat = tenant.getAutoReplyFormat();
                    Shop shop = shopMapper.selectByPrimaryKey(friend.getTenantId());
                    if (autoReplyFormat == null) {
                        content = "您好，我是" + shop.getShopName() + "在线客服，请问有什么可以帮您？";
                        if (!StringUtils.isEmpty(shop.getAfterSalePhone())) {
                            content = content + "服务电话：" + shop.getAfterSalePhone();
                        } else if (!StringUtils.isEmpty(shop.getPhone())) {
                            content = content + "服务电话：" + shop.getPhone();
                        }
                    } else {
                        Map replyFormatMap = new HashMap();
                        replyFormatMap.put("shopName", shop.getShopName());
                        replyFormatMap.put("serviceName", friend.getServiceName());
                        replyFormatMap.put("phone", StringUtils.isEmpty(shop.getAfterSalePhone()) ? shop.getPhone() : shop.getAfterSalePhone());
                        content = placeholderResolver.resolveByMap(autoReplyFormat, replyFormatMap);
                    }
                }
                JSONObject msg = new JSONObject();
                msg.put("isMy", false);
                msg.put("headUrl", friend.getAvatar());
                msg.put("type", "text");
                msg.put("content", content);
                msg.put("friendId", userObj.getId());
                msg.put("tenantId", userObj.getTenantId());
                msg.put("friendAvatar", userObj.getAvatar());
                msg.put("friendNickname", userObj.getNickname());
                msg.put("friendServiceName", userObj.getServiceName());
                msg.put("nickname", friend.getNickname());
                msg.put("serviceName", friend.getServiceName());
                msg.put("eventType", "chat");

                sendMessageToFriend(friend.getId(), msg, map);
            }
        }
    }

    /**
     * 发送消息给好友
     *
     * @param userId
     * @param obj
     * @param map
     */
    @Override
    public void sendMessageToFriend(Long userId, JSONObject obj, ConcurrentHashMap<Long, WebSocketServer> map) throws IOException {
        //追加发送人(防止串改)
        obj.put("msgUserId", userId);
        Long friendId = obj.getLongValue("friendId");
        Long tenantId = obj.getLongValue("tenantId");

        Map<String, String> feature = new HashMap<>();
        if ("voice".equals(obj.getString("type"))) {
            feature.put("duration", obj.getString("duration"));
        }

        Msg msg = Msg.builder()
                .chatId(userId < friendId ? userId + ":" + friendId : friendId + ":" + userId)
                .fromId(userId)
                .toId(friendId)
                .tenantId(tenantId)
                .msgType((byte) 0)
                .content(obj.getString("content"))
                .type(obj.getString("type"))
                .feature(JSON.toJSONString(feature))
                .isRead(MsgReadStatusEnum.UNREAD.getStatus())
                .createdAt(new Date())
                .build();
        msgMapper.insert(msg);

        obj.put("id", msg.getId());
        obj.put("userId", userId);
        obj.put("isRead", MsgReadStatusEnum.UNREAD.getStatus());
        obj.put("timestamp", System.currentTimeMillis());

        if (map.containsKey(friendId)) {
            map.get(friendId).sendMessage(buildMessage(MessageTypeEnum.MESSAGE, obj));
        }
        map.get(userId).sendMessage(buildMessage(MessageTypeEnum.MESSAGE, obj));
        log.debug(JSON.toJSONString(obj));
        jpushService.handleMessage(obj);
    }

    @Override
    public boolean onUserLoginVerify(String token) {
        final String authToken = token.substring(tokenHead.length());
        if (authToken != null) {
            String username = jwtTokenUtil.getUsernameFromToken(authToken);
            log.info("checking authentication " + username);

            // 登录的账号要和token中的账号相同
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                    return true;
                }
            }
        }
        return false;
    }


    @Override
    public Long getUserId(String token) {
        final String authToken = token.substring(tokenHead.length());
        if (authToken != null) {
            String username = jwtTokenUtil.getUsernameFromToken(authToken);
            return msgUserMapper.findOneIdByUsername(username);
        }
        return 0L;
    }
}